package com.spring.security.client.browser.session;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.security.core.properties.SecurityConstants;
import com.spring.security.core.properties.SecurityProperties;
import com.spring.security.core.support.ServerResponse;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * session失效处理器
 *
 * @author_seabed_moon
 */
@Log4j2
public class AbstractSessionStrategy {

    /**
     * 跳转的url
     */
    private String destinationUrl;
    /**
     * 系统配置信息
     */
    private SecurityProperties securityProperties;
    /**
     * 重定向策略
     */
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    /**
     * 跳转前是否创建新的session
     */
    private boolean createNewSession = true;

    private ObjectMapper objectMapper = new ObjectMapper();

    /**
     * @param securityProperties
     */
    public AbstractSessionStrategy(SecurityProperties securityProperties) {
        String invalidSessionUrl = securityProperties.getBrowser().getSession().getSessionInvalidUrl();
        Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
        Assert.isTrue(StringUtils.endsWithIgnoreCase(invalidSessionUrl, SecurityConstants.DEFAULT_SUFFIX), "url must end with '.html'");
        this.destinationUrl = invalidSessionUrl;
        this.securityProperties = securityProperties;
    }

    /**
     * @see org.springframework.security.web.session.InvalidSessionStrategy
     * onInvalidSessionDetected(javax.servlet.http.HttpServletRequest,
     * javax.servlet.http.HttpServletResponse)
     */
    protected void onSessionInvalid(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

        String originHeader = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin", originHeader);
        response.addHeader("Access-Control-Allow-Headers", SecurityConstants.DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS);
        response.addHeader("Access-Control-Allow-Credentials", SecurityConstants.DEFAULT_ACCESS_CONTROL_ALLOW_CREDENTIALS);
        response.setHeader("Access-Control-Allow-Methods", SecurityConstants.DEFAULT_ACCESS_CONTROL_ALLOW_METHODS);

        if (createNewSession) {
            request.getSession();
        }

        String sourceUrl = request.getRequestURI();
        String targetUrl;

        if (StringUtils.endsWithIgnoreCase(sourceUrl, SecurityConstants.DEFAULT_SUFFIX)) {
            if (StringUtils.equals(sourceUrl, securityProperties.getBrowser().getSignInPage())
                    || StringUtils.equals(sourceUrl, securityProperties.getBrowser().getSignOutUrl())) {
                targetUrl = sourceUrl;
            } else {
                targetUrl = destinationUrl;
            }
            log.info("session已失效,跳转到:{}", targetUrl);
            redirectStrategy.sendRedirect(request, response, targetUrl);
        } else {
            String servletPath = request.getServletPath();
            List<String> urlList = Arrays.asList(securityProperties.getWhite().getAllUrls());
            if (!urlList.contains(servletPath)) {
                Object result = buildResponseContent(request);
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.getWriter().write(objectMapper.writeValueAsString(result));
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                return;
            }
            log.info("转发URL:{}", servletPath);
            request.getRequestDispatcher(servletPath).forward(request, response);
        }

    }

    /**
     * @param request
     * @return
     */
    protected Object buildResponseContent(HttpServletRequest request) {
        String message = "session已失效";
        log.info(message);
        if (isConcurrency()) {
            message = message + "，有可能是并发登录导致的";
            log.info(message);
        }
        return new ServerResponse(HttpStatus.UNAUTHORIZED.value(), message, "");
    }

    /**
     * session失效是否是并发导致的
     *
     * @return
     */
    protected boolean isConcurrency() {
        return false;
    }

    /**
     * Determines whether a new session should be created before redirecting (to
     * avoid possible looping issues where the same session ID is sent with the
     * redirected request). Alternatively, ensure that the configured URL does
     * not pass through the {@code SessionManagementFilter}.
     *
     * @param createNewSession defaults to {@code true}.
     */
    public void setCreateNewSession(boolean createNewSession) {
        this.createNewSession = createNewSession;
    }

}
